/*********************************************************************************
 *      Copyright:  (C) 2015 songyong<handy_skyoutlook.com>
 *                  All rights reserved.
 *
 *       Filename:  ds18b20.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2015年08月25日)
 *         Author:  sky <handy_sky@outlook.com>
 *      ChangeLog:  1, Release initial version on "2015年08月25日 09时12分04秒"
 *                 
 ********************************************************************************/
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/fs.h>
#include<linux/init.h>
#include<linux/delay.h>
#include<linux/gpio.h>
#include<linux/types.h>
#include<asm/irq.h>
#include<mach/regs-gpio.h>
#include<mach/hardware.h>
#include<linux/device.h>
#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<linux/errno.h>
#include<asm/uaccess.h>

#define DQ S3C2410_GPG(0)
#define INPUT S3C2410_GPIO_INPUT
#define OUTPUT S3C2410_GPIO_OUTPUT

#define D_MAJOR 0
#define D_MINOR 0
#define DEV_NAME "ds18b20" 


static int ds18b20_major = D_MAJOR;
static int ds18b20_minor = D_MINOR;

struct ds18b20_device
{
    struct class    *sy_class;
    struct cdev     cdev;
};
static struct ds18b20_device   dev;

static unsigned int ds18b20_reset(void)
{
    int err;

    s3c2410_gpio_cfgpin(DQ, OUTPUT);
    s3c2410_gpio_pullup(DQ, 0);

    s3c2410_gpio_setpin(DQ, 1);
        udelay(10);
    s3c2410_gpio_setpin(DQ, 0);
        udelay(600);

    s3c2410_gpio_setpin(DQ, 1);
        udelay(60);

    s3c2410_gpio_cfgpin(DQ, INPUT);
        udelay(400);
    err = s3c2410_gpio_getpin(DQ);
    return err;
}

static unsigned int ds18b20_write(unsigned char data)
{
    unsigned int i;
    s3c2410_gpio_cfgpin(DQ, OUTPUT);
    for (i = 0; i < 8; i++)
    {
        s3c2410_gpio_setpin(DQ, 0);
        udelay(5);
        if(data & 0x01)
        {
            s3c2410_gpio_setpin(DQ ,1);
                udelay(60);
        }
        else udelay(60);
        data >>= 1;
        s3c2410_gpio_setpin(DQ, 1);
        udelay(1);
    }
    return 2;
}

static unsigned int ds18b20_read(void)
{
    unsigned int i ;
    unsigned char data = 0x00;

    for (i =0; i < 8 ; i++)
    {
        s3c2410_gpio_cfgpin(DQ, OUTPUT);
        s3c2410_gpio_setpin(DQ, 1);
        udelay(1);
        s3c2410_gpio_setpin(DQ, 0);
        udelay(2);
        s3c2410_gpio_setpin(DQ, 1);
        s3c2410_gpio_cfgpin(DQ, INPUT);
        data >>= 1;
        if(0 != s3c2410_gpio_getpin(DQ))
            data |= 0x80;
        udelay(60);
    }
    return data;
}

static ssize_t read_ds18b20(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    unsigned char Data[2] = {0x00, 0x00};
    unsigned long err; 
    int flag;

    flag = ds18b20_reset();
    if(flag <= 0)
    {
        printk("ds18b20 init fail!\n");
        return -1;
    }

    ds18b20_write(0xcc);
    ds18b20_write(0x44);

    ds18b20_reset();
    if(flag <= 0)
    {
        printk("ds18b20 init fail!\n");
        return -1;
    }

    ds18b20_write(0xcc);
    ds18b20_write(0xbe);

    Data[0] = ds18b20_read();
    Data[1] = ds18b20_read();
    ds18b20_reset();
    err = copy_to_user(buf, Data, sizeof(Data));
    return err? -EFAULT:count;
}
#if 1 
static int open_ds18b20(struct inode *inode, struct file *filp)
{
    int flag = 0;
    int a = 0;

    a = gpio_request(S3C2410_GPG(0),"GPG_0");
    if(a)
    {
        printk("Request GPIO failed!\n");
    }
    flag = ds18b20_reset();
    if(flag)
    {
        printk("open ds18b20 successful!\n");
    }
    else printk("open ds18b20 failed!\n");
    return 0;
}
#endif

static int release_ds18b20(struct inode *inode, struct file *filp)
{
    return 0;
}

static struct file_operations fops={
    .owner  = THIS_MODULE,
    .read   = read_ds18b20,
    .open   = open_ds18b20,
    .release = release_ds18b20,
};

static int __init ds18b20_init(void)
{
    int result,err;
    dev_t devno = 0;

    if(ds18b20_major)
    {
        devno = MKDEV(ds18b20_major, ds18b20_minor);
        result = register_chrdev_region(devno, 1, DEV_NAME);
    }
    else{
        result = alloc_chrdev_region(&devno, ds18b20_minor, 1, DEV_NAME);
        ds18b20_major = MAJOR(devno);
    }
    if(result < 0)
    {
        printk(KERN_ERR "%s can't use major %d\n",DEV_NAME, ds18b20_major);
    }
    printk("%s use major %d\n",DEV_NAME, ds18b20_major);

    cdev_init(&dev.cdev,&fops);
    dev.cdev.owner = THIS_MODULE;
    err = cdev_add(&dev.cdev, devno, 1);
    if(err)
    {
        printk(KERN_NOTICE"ERROR %d add ds18b20\n",err);
        goto ERROR;
    }
    dev.sy_class   = class_create(THIS_MODULE, DEV_NAME);
    device_create(dev.sy_class, NULL, MKDEV(ds18b20_major, ds18b20_minor), NULL, DEV_NAME);
    printk(KERN_NOTICE"Ds18b20 is ok!\n");
    return 0;
ERROR:
    printk(KERN_ERR"%s driver installed failure.\n",DEV_NAME);
    cdev_del(&dev.cdev);
    unregister_chrdev_region(devno, 1);
    return err;

}
static void __exit ds18b20_exit(void)
{
    dev_t devno = MKDEV(ds18b20_major, 0);

    cdev_del(&dev.cdev);
    device_destroy(dev.sy_class,devno);
    class_destroy(dev.sy_class);

    unregister_chrdev_region(devno, 1);
    printk(KERN_NOTICE"bye ds18b20!\n");
    gpio_free(S3C2410_GPG(0));
}

module_init(ds18b20_init);
module_exit(ds18b20_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("handy_sky@outlook.com");
